﻿using System.Collections.Generic;
using System.Diagnostics;
using Game.Command;
using Game.Common;
using Game.Map;
using Game.Map.Extensions;
using Game.Objects;

namespace Game.Controller.Internal
{
    class MoveStatagy
    {
        readonly IGameMapView _GameMap;
        readonly List<CostField> _OpenList = new List<CostField>();
        readonly List<CostField> _ClosedFields = new List<CostField>();
        
        public MoveStatagy(IGameMapView map)
        {
            _GameMap = map;
        }

        class CostField 
        {
            public IMapFieldView Field { get; set; }
            public double Cost { get; set; }

            public override bool Equals(object obj)
            {
                var field = obj as CostField;
                Debug.Assert(field != null, "field != null");
                return (field.Field.Point == Field.Point);
            }

            public override int GetHashCode()
            {
                return Field.Point.GetHashCode();
            }

            public override string ToString()
            {
                return "" + Cost;
            }
        }

        class CostFieldComparor : IComparer<CostField>
        {
            public int Compare(CostField x, CostField y)
            {
                if (x.Cost == y.Cost)
                {
                    return 0;
                }
                return (x.Cost > y.Cost ? 1 : -1);
            }
        }

        public void AssignCommand(
            IMapFieldView field, 
            GameTurn turn)
        {
            var adjacent = GetAdjacentFields(field);

            while (adjacent.Count > 0)
            {
                adjacent.Sort(new CostFieldComparor() );
                
                var cursor = adjacent[0];
                adjacent.Remove(cursor);

                Process(field, adjacent, cursor);
            }

            AssignCommands(field, field.VehicleX().Vehicle);
        }

        void Process(
            IMapFieldView field, 
            List<CostField> adjacent, 
            CostField cursor)
        {
            if (_ClosedFields.Contains(cursor) || _OpenList.Contains(cursor))
            {
                return;
            }

            var obj = cursor.Field.ObjectX().GameObject;
            if (obj != null && obj.Building != null)
            {
                return;
            }

            if (cursor.Field.VehicleX().Vehicle != null)
            {
                return;
            }

            var vehicle = field.VehicleX().Vehicle;
            Debug.Assert(vehicle != null, "vehicle != null");
            double availableCost = vehicle.Movement.Value;

            var terrainAccess = vehicle.TerrainAccess;
            if (!terrainAccess.IsAccessible(cursor.Field.TerrianX().TerrainType.TerrainBits))
            {
                _ClosedFields.Add(cursor);
            }
            else if (cursor.Cost > availableCost)
            {
                _ClosedFields.Add(cursor);
            }
            else
            {
                _OpenList.Add(cursor);
                AddAdjacentFields(adjacent, cursor);
            }
        }

        void AddAdjacentFields(
            ICollection<CostField> adjacent, 
            CostField cursor)
        {
            var list = MapUtil.GetAdjacentViewFields(
                _GameMap, cursor.Field.Point);

            foreach (var f in list)
            {
                var costField2 = new CostField
                                     {
                                         Field = f,
                                         Cost = f.TerrianX().TerrainCost + cursor.Cost,
                                     };

                adjacent.Add(costField2);
            }
        }

        void AssignCommands(
            IMapFieldView field, 
            Vehicle vehicle)
        {
            foreach (var cursor in _OpenList)
            {
                var path2 = new GamePath(
                    field.Point, cursor.Field.Point);
                cursor.Field.CommandX().Command = 
                    Command.Factory.CreateMoveUnitCommand(
                        _GameMap, path2, vehicle);
                cursor.Field.ViewX().GameImage = GameImage.MoveArrow;
            }
        }

        List<CostField> GetAdjacentFields(IMapFieldView field)
        {
            var adjacent = new List<CostField>();
            var fields = MapUtil.GetAdjacentViewFields(_GameMap, field.Point);

            foreach (var cursor in fields)
            {
                if (cursor.IsVisible == false)
                {
                    continue;
                }

                if (cursor.Point == field.Point)
                {
                    continue;
                }

                if (cursor.VehicleX().Vehicle != null)
                {
                    continue;
                }

                var costField = new CostField
                                    {
                                        Field = cursor,
                                        Cost = cursor.TerrianX().TerrainCost
                                    };
                adjacent.Add(costField);
            }

            return adjacent;
        }
    }
}
